# encoding: utf-8
"""
Apps Modules
=======

Modules enable logical resource separation.

You may control enabled modules by modifying ``ENABLED_MODULES`` config
variable.
"""
import traceback

import re

import app
from app.extensions import db,log
from app.extensions.api import api_v1
from app.modules.apps import resources
from app.modules.apps.models import App
from app.modules.apps.pipelines import ContainerAppEvent, do_init_container_app, init_container_app_pipeline, \
    set_app_runtime_info, init_app_piplelines
from app.modules.common import pipeline_manager
from app.modules.common.pipelines import PipelineStatusListner, PipelineOnStartedEvent, PipelineOnDoneEvent, \
    PipelineOnFailedEvent, PipelineProgressChangedEvent
from app.modules.notify import notifier

api_v1.add_namespace(resources.port_free_lock_api)


# 初始化app模块的静态对象,比如app操作监听器和container_app.
def init_app(app, **kwargs):
    from . import models, resources

    api_v1.add_namespace(resources.api)

    with app.app_context():
        container_pipeline_listner = ContainerAppPipelineListner(app_ctx=app)
        pipeline_manager.subscribe(container_pipeline_listner)

        list = App.query.filter_by(disabled=0).order_by(App.starting_order.asc()).all()
        # 重启时 初始化应用状态running为unavailable
        for _app in list:
            if _app.status == 'running':
                log.info("update app status :%s" % _app.app_id)
                _app.status = 'unavailable'
            if _app.db_status == 'running':
                log.info("update app db status :%s" % _app.app_id)
                _app.db_status = 'unavailable'
        db.session.commit()

        init_app_piplelines(list)


# 对ContainerApp操作的定义监听器.
class ContainerAppPipelineListner(PipelineStatusListner):
    def __init__(self, app_ctx):
        super(ContainerAppPipelineListner, self).__init__()
        self.app_context = app_ctx.app_context()

    def on_event(self, event):

        log.info("On notify for pipeline :%s" % event.pipeline.name)

        _pipeline_name = event.pipeline.name
        _pipeline = event.pipeline

        s = re.search("^[A-Za-z0-9]*:[A-Za-z0-9]*(-.*)*_(restart|install|stop)", _pipeline_name, re.M)

        if s is None:
            # 证明不是containerapp的消息,丢弃.
            return

        _is_install_pipe = False
        _install_pipe = re.search("^[A-Za-z0-9]*:[A-Za-z0-9]*(-.*)*_(install)", _pipeline_name, re.M)

        if _install_pipe is not None:
            _is_install_pipe = True

        _app_id = getattr(event.pipeline, '_app_id')
        _app = self.get_app_info(_app_id)

        if _app is None:
            log.error("Can't find app by app_id:%s" % _app_id)
            raise Exception("Can't find app by app_id:%s" % _app_id)

        _container_app = app.container_pool.get(_app_id)

        if _container_app is None:
            log.error("Can't find container by app_id:%s" % _app_id)
            raise Exception("Can't find container by app_id:%s" % _app_id)

        # 根据containerapp的属性,
        _version = getattr(_container_app, '_version', '-1')
        _app_package_create_date = getattr(_container_app, '_app_package_create_date', '')

        try:
            _app = set_app_runtime_info(_app, check_running_accurately=True)

            if isinstance(event, PipelineOnStartedEvent):

                _last_msg = "[INFO]%s 准备执行." % _pipeline_name

                if _is_install_pipe:
                    self.do_update_app(app_id=_app_id, pipeline_name=_pipeline_name, version=_version, status='running',
                                       last_msg=_last_msg, app_package_create_date=_app_package_create_date)

                _app.status = 'running'
                _app.last_msg = _last_msg
                _app.app_package_create_date = _app_package_create_date

                log.info("Send noitfy of pipe startup for app_id:%s" % _app_id)
                notifier.send_message(ContainerAppEvent(pipeline=_pipeline, app=_app))

            elif isinstance(event, PipelineOnDoneEvent):

                # 当某个任务结束时, 更新app表的状态. 如果是install任务则同时要更新版本号.
                _last_msg = "[INFO]%s 执行完毕." % _pipeline_name

                if _is_install_pipe:
                    self.do_update_app(app_id=_app_id, pipeline_name=_pipeline_name, version=_version, status='ready',
                                       last_msg=_last_msg, app_package_create_date=_app_package_create_date)

                _app.running_pipeline = None
                _app.status = 'ready'
                _app.last_msg = _last_msg
                _app.app_package_create_date = _app_package_create_date

                log.info("Send noitfy of pipe done for app_id:%s" % _app_id)
                notifier.send_message(ContainerAppEvent(pipeline=_pipeline, app=_app))


            elif isinstance(event, PipelineOnFailedEvent):
                _version = getattr(_app, 'version', '-1')

                _last_msg = "[ERROR]%s 发生异常 异常信息:%s" % (_pipeline_name, event.err_msg)

                # 当某个任务结束时, 更新app表的状态. 如果是install任务则同时要更新版本号.


                self.do_update_app(app_id=_app_id, pipeline_name=_pipeline_name, version=_version,
                                   status='unavailable',
                                   last_msg=_last_msg, app_package_create_date=_app_package_create_date)

                _app.running_pipeline = None
                _app.status = 'unavailable'
                _app.last_msg = _last_msg
                _app.app_package_create_date = _app_package_create_date
                _app.is_running = False

                log.info("Send noitfy of pipe error for app_id:%s" % _app_id)
                notifier.send_message(ContainerAppEvent(pipeline=_pipeline, app=_app))

            elif isinstance(event, PipelineProgressChangedEvent):

                pass
            else:
                pass

        except:

            _last_msg = 'Oh nooooooo! We\'ve got a error during deal notify messge! pipeline:%s ' % _pipeline_name

            log.error(_last_msg)

            log.info("Send noitfy of pipe notify error for app_id:%s  too bad, realy bad...." % _app_id)
            notifier.send_message(ContainerAppEvent(pipeline=_pipeline, app=_app))

        log.info("Finish notify for pipeline :%s" % event.pipeline.name)

    def get_app_info(self, app_id):

        with self.app_context:
            return App.query.filter_by(app_id=app_id).filter_by(disabled=0).first()

    # 将app对应的pipleline的执行过程记录到app表中
    def do_update_app(self, app_id, pipeline_name, version, status, last_msg=None, app_package_create_date=None):

        with self.app_context:

            try:
                _app = App.query.filter_by(app_id=app_id).filter_by(disabled=0).first()

                if _app is not None:
                    _app.status = status
                    _app.last_msg = unicode(last_msg)

                    _app.app_package_create_date = app_package_create_date
                    _app.version = version

                    log.info('%s状态更新: status=%s, version=%s, last_msg=%s,app_package_create_date=%s  ' % (
                        _app.app_id, status, version, last_msg, app_package_create_date))
                db.session.commit()

            except Exception, e:
                log.error(traceback.format_exc())
                raise e
